package com.icoolkj.kevin;

import com.icoolkj.kevin.config.AppConfig;
import com.icoolkj.kevin.service.UserService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @author icoolkj
 * @version 1.0
 * @description
 * @createDate 2025/01/23 19:10
 */
public class ApplicationContextTest {
	public static void main(String[] args) {
		// spring framework
		// 扫描  创建非懒加载的单例 Bean
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
//		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
//		System.out.println(context.getBean("userService")); // 继承 BeanDefinition 原型情况
//		System.out.println(context.getBean("userService"));
//		System.out.println(context.getBean("userService"));


		// spring 中的 Bean 对象和自己创建的对象有啥区别呢？
//		UserService userService = (UserService) context.getBean("userService");
//		userService.test();
//
//
//		//Spring 事务的代理对象
//		// 代理逻辑
//		class UserServiceProxy extends UserService{
//
//			UserService target;
//			public void test(){
//				//1、@Transactional 判断有没有这个注解
//				//2、创建一个数据库的连接（事务管理器 PlatformTransactionManager 创建）
//				//3、修改 conn.autocommit = false
//				target.saveUser(); //2、执行父类方法、
//
//				//conn.commit()
//				//conn.rollback()
//			}
//		}



		//基于 cglib 动态代理，继承，父子类实现代理
		//UserSericeProxy --> 代理对象（注意：代理对象的 orderService 没有值） -->代理对象 target = 普通对象（普通对象就会有 orderService 属性）
		// 代理对象 test()，代理对象 Bean，然后调用普通对象的 test() 方法，
		// 先执行 AOP 切面的逻辑 @Before
//		class UserServiceProxy extends UserService{
//
//			UserService target;
//			public void test(){
//				// 1、执行 @Before
//				target.test(); //2、执行父类方法
//			}
//		}
		// 切面也是一个 Bean，切面 Bean；如何判断某个对象要不要进行 AOP 呢？
		// 1、找出所有的切面 Bean，也就是有 @Aspect 注解的 Bean
		// 2、遍历所有的切面 Bean，
		// 3、遍历方法，看看有没有 @Before @After @Around 注解，
		// 4、然后看注解里面的表达式和当前创建的 Bean 对象是否匹配
		// 5、如果匹配，说明当前 Bean 是要进行 AOP 的；然后缓存这些方法，缓存 map。
		// 6、执行的时候直接从缓存 map 里面拿出所有方法来执行



		// spring 默认找无参构造方法，如果多个构造方法但没有无参的就会报错（多个找默认的，找不到就报错，可以指定用那个构造方法 @Autowired），如果只有一个构造方法就可以使用（如果是有参的也可以正常使用）。
		// 例如 会从 spring 容器里面找构造方法的 OrderService Bean， 如果通过名字找回有问题，先根据类型找（可能找到多个）

	/*	为什么 @Autowired 注解能正常工作，而构造器注入会报错？
		1. @Autowired 的行为
		默认情况下，@Autowired 注解会按照类型（byType）进行注入。
		如果有多个相同类型的 Bean（例如多个 OrderService），Spring 会尝试通过 Bean 名称 进一步匹配。
		如果 @Autowired 的字段名与 Bean 的名称相同（例如字段名为 orderServiceA，而 Bean 名称也为 orderServiceA），Spring 可以自动匹配成功。
		2. 构造器注入的行为
		在构造器注入中，Spring 无法通过参数名称直接匹配 Bean 名称。
		因为构造器参数的名称在运行时会被编译器擦除，Spring 无法知道参数名，只有参数类型。
		当 Spring 发现多个 OrderService 类型的 Bean 时，它会因为无法决定应该注入哪个 Bean 而报错。
	*/
//		System.out.println(context.getBean("orderService"));
//		System.out.println(context.getBean("orderService1"));
//		System.out.println(context.getBean("orderService2"));


		// Bean 的创建生命周期
		// UserService.class -->无参构造方法（推断构造方法）-->对象-->依赖注入(属性赋值)--Autowired  -->初始化前（@PostConstruct） -->初始化（InitializingBean） --> 初始化后（AOP）-->代理对象-->  Bean（代理对象）
		// Bean 的销毁生命周期

		// 伪代码依赖注入，给 Autowired 注解的属性赋值。
//		for (Field field : userService.getClass().getFields()) {
//			if(field.isAnnotationPresent(Autowired.class)){
//				field.set(userService, ???);
//			}
//		}

		// 伪代码，bean 初始化前调用的方法
//		for (Method method : userService.getClass().getMethods()) {
//			if (method.isAnnotationPresent(PostConstruct.class)){
//				method.invoke(userService, null);
//			}
//		}

		//伪代码，通过实现 InitializingBean 接口的方式在 bean 初始化前调用 afterPropertiesSet 方法。
//		if(userService instanceof InitializingBean){
//			InitializingBean service = (InitializingBean) userService;
//			try {
//				service.afterPropertiesSet();
//			} catch (Exception e) {
//				throw new RuntimeException(e);
//			}
//		}


	}
}
